package notify

import (
	"errors"
	"fmt"
	"log"
	"sync"
	"time"
)

type Notify struct {
	queue chan GroupReq
	stop chan struct{}
	group map[int64]*Group
	mux sync.Mutex
	config *Config
	debug bool
	expireSec int32
	cache *NotifyCache

}
func NewNotify(conf *Config) *Notify{
	if conf == nil {
		panic("conf is panic")
	}
	if conf.Concurrency<1{
		conf.Concurrency = 3
	}
	if conf.MaxQuene<1 {
		conf.MaxQuene = 5
	}
	if conf.GroupExpire<1 {
		conf.GroupExpire = Duration(time.Duration(time.Second*86400*7))//默认保存分组状态
	}

	if conf.Group == nil {
		conf.Group = &GroupConfig{
			WxUserInfoQueue: 1024,
			WxMsgQueue:      1024,
			WxMsgWorker:     20,
			AppMsgQueue:     1024,
			AppMsgWorker:    1,
			SmsMsgQueue:     1024,
			SmsMsgWorker:    1,
			BatchNum: 100,
			UserPushWorker: 1,
		}
	} else {
		if conf.Group.WxUserInfoQueue < 1 {
			conf.Group.WxUserInfoQueue = 8
		}
		if conf.Group.WxMsgQueue < 1 {
			conf.Group.WxMsgQueue = 1024
		}
		if conf.Group.WxMsgWorker < 1 {
			conf.Group.WxMsgWorker = 5
		}
		if conf.Group.AppMsgQueue < 1 {
			conf.Group.AppMsgQueue = 1024
		}
		if conf.Group.AppMsgWorker < 1 {
			conf.Group.AppMsgWorker = 1
		}
		if conf.Group.SmsMsgQueue < 1 {
			conf.Group.SmsMsgQueue = 1024
		}
		if conf.Group.SmsMsgWorker < 1 {
			conf.Group.SmsMsgWorker = 1
		}
		if conf.Group.BatchNum < 1 {
			conf.Group.BatchNum = 100
		}
		if conf.Group.UserPushWorker <1 {
			conf.Group.UserPushWorker = 1
		}
	}
	if conf.Provider == nil {
		panic("Provider is nil")
	}
	log.Printf("config(%+v),group(%+v),provider(%+v)",conf,conf.Group,conf.Provider)
	n := &Notify{
		queue: make(chan GroupReq,conf.MaxQuene),
		debug: conf.Debug,
		stop: make(chan struct{}),
		group: make(map[int64]*Group,0),
		mux:sync.Mutex{},
		config: conf,
		expireSec: int32(time.Duration(conf.GroupExpire) / time.Second),
	}
	n.cache = NewNotifyCache(n.config.Redis,n.expireSec)
	return n
}


func (n *Notify) Push(req GroupReq) error {
	if req.LoadUserFun == nil {
		return errors.New("groupReq LoadUserFun is nil")
	}
	if req.SyncPrgFun == nil {
		return errors.New("SyncPrgFun LoadUserFun is nil")
	}
	select {
	case n.queue<-req:
		return nil
	default:
		return fmt.Errorf("任务数量超过指定数量")
	}
}
func (n *Notify) Stop()  {
	n.queue = nil //不在接收消息。如果里面有消息要怎么做？？？？？？？
	n.mux.Lock()
	for _,g := range n.group {
		g.Stop()
	}
	n.group = map[int64]*Group{}
	n.mux.Unlock()
	close(n.stop)
}
func (n *Notify) Start() error {
	if n.config == nil {
		return errors.New("config is nil")
	}
	if n.config.Provider == nil {
		return errors.New("config provider is nil")
	}
	if n.config.Group == nil {
		return errors.New("config Group is nil")
	}

	log.Println("start group with worker:",n.config.Concurrency," debug:",n.debug)

	wg := sync.WaitGroup{}
	wg.Add(n.config.Concurrency)
	for i:=0;i<n.config.Concurrency;i++ {
		go func(num int) {
			log.Println("start group worker num:",num)
			defer wg.Done()
			var g GroupReq
			for{
				select {
				case g= <-n.queue:
				case <-n.stop:
					log.Println("end group worker num:",num)
					return
				default:
					time.Sleep(time.Second*1)
					continue
				}
				group := NewGroup(n.config,n.cache)
				group.groupId = g.Id
				n.addGroup(g.Id,group)
				group.do(g)
				n.removeGroup(g.Id)

			}
		}(i)
	}
	wg.Wait()
	return nil
}
func (n *Notify) StopGroup(gid int64) bool{
	n.mux.Lock()
	defer n.mux.Unlock()
	group,ok := n.group[gid]
	if !ok {
		log.Printf("stop groupId(%d) don't exisit",gid)
		return true
	}
	group.Stop()
	delete(n.group,gid)
	log.Printf("stop groupId(%d) ok",gid)
	return true
}
func (n *Notify) RestartGroup(gid int64) bool {
	return true
}
func (n *Notify) SetKeyVal(key,val string) error{
	return n.cache.SetKeyValue(key,val)
}
func (n *Notify) GetKeyVal(key string) (string,error) {
	return n.cache.GetKeyValue(key)
}
func (n *Notify) addGroup(gid int64,group *Group) error {
	n.mux.Lock()
	defer n.mux.Unlock()
	if _,ok := n.group[gid];ok {
		return fmt.Errorf("repeat consume groupId(%d)",gid)
	}
	n.group[gid] = group
	return nil
}

func (n *Notify) removeGroup(gid int64)  {
	n.mux.Lock()
	defer n.mux.Unlock()
	if _,ok := n.group[gid];ok {
		delete(n.group,gid)
	}
}

func getAppUser(userInfos []UserInfo) map[int]string{
	users := make(map[int]string,len(userInfos))
	for _,user :=range userInfos{
		if user.App == ""{
			continue
		}
		users[user.Pos] = user.App
	}
	return users
}
func getMobile(userInfos []UserInfo) map[int]string{
	users := make(map[int]string,len(userInfos))
	for _,user :=range userInfos{
		if user.Mobile == ""{
			continue
		}
		users[user.Pos] = user.Mobile
	}
	return users
}
func getOpenId(userInfos []UserInfo) map[int]string{
	users := make(map[int]string,len(userInfos))
	for _,user :=range userInfos{
		if user.OpenId == ""{
			continue
		}
		users[user.Pos] = user.OpenId
	}
	return users
}






